Progressive Disclosure: Increasing Agentic System reliability with System Messages and Variable Tools

A few months after we shipped agentic multi-channel collections outreach, the system was doing real work: conversations were flowing, channels were coordinated, and the brittle SMS-only past was behind us. Then a quieter problem showed up. The workflow was stable, but routing was not quite sharp enough. The conversation graph was doing its job, yet we were still seeing cases where the model drifted at transitions: the wrong emphasis at a handoff, a tool call that made sense in isolation but not in sequence.

This post is about what we changed, and why it hinged on an idea that has become central in the LLM era: progressive disclosure—keeping context intentionally small, and expanding it only when the moment demands it.


What progressive disclosure means here

The phrase comes from interface design: show people only what they need right now, and reveal more as they go deeper. That definition still matters, but in agent systems the “user” on the receiving end is often the model. The constraint that forces the pattern is the context window.

Modern models advertise enormous windows, and they are genuinely useful. Still, there are hard limits in practice: cost, latency, and the simple fact that stuffing more text into a window does not mean the model will track every detail with equal fidelity. Research on long-context behavior—for example, the “lost in the middle” finding in Lost in the Middle: How Language Models Use Long Contexts (Liu et al.)—lines up with what you feel in production: performance is not uniform across the full span. The longer and noisier the context, the easier it is for instructions to blur together and for the model to overweight the wrong thing.

So progressive disclosure, in this setting, is a discipline: hold the context as small as you can, and add instructions, state, and affordances just in time—when a specific step or event makes them relevant. That is how you steer behavior more consistently than you can by dumping every rule and every tool into one flat prompt.

If you want broader background, two useful starting points are the classic UX treatment from Nielsen Norman Group on progressive disclosure and a more agent-oriented angle such as Progressive Disclosure for Agents from SixDegree. The vocabulary differs by author, but the through-line is the same: density and timing matter as much as raw token count.


Where we started: a graph that was already “disclosing”

When we built the new outreach system, we modeled the business as a conversation graph: nodes for phases of the flow, edges for allowed transitions, and at each node a focused role—introduction, triage, routing, policy-bound execution, and so on. In the earlier write-up, I described how that structure helped us govern what context and tools applied at each step.

That design was already aligned with progressive disclosure in a structural sense. Each node was supposed to expose only the information and capabilities relevant to the task at hand: a dedicated introduction agent here, a triage or routing agent there, so the model was not trying to juggle the entire policy surface at once.

After a couple of weeks in production, though, it was clear we needed more control at the edges—especially anywhere the graph handed work from one Agent to another. The graph was right; routing accuracy was the gap.


Change 1: System messages as just-in-time commands

Early on, a common pattern looked like this: the model called a tool, the tool returned something minimal—often a generic success or failure—and the model continued however it interpreted the next turn. That works for stateless helpers. It does not work as well when the tool is really a navigation primitive: a transfer, a branch decision, a gate that is supposed to deterministically set up the next phase.

For those graph-like transitions, we needed the tool response to do more than confirm execution. We needed it to surface an immediate, explicit instruction—almost a command—about what should happen next. So we introduced system messages: short, authoritative lines tied to specific events in our stack, delivered at the moment the event fires.

For example, once we verified that we were speaking to the right party, we did not want the model to improvise the next move. We wanted a reliable handoff into triage. The tool layer and orchestration could emit a system message in that instant—essentially, “verification succeeded; now transfer to the triage agent”—so the model’s very next reasoning step was anchored to that fact.

That pattern generalized. Anywhere the environment produced a crisp signal (verification, channel preference, a policy outcome, a completed sub-flow), we could attach a just-in-time system message instead of hoping the model would infer the same thing from a vague “OK” string. The progressive disclosure angle is obvious in hindsight: we were no longer relying on the model to remember the implication of a tool result buried in chat history. We were re-disclosing the implication at the moment it mattered.


Change 2: Variable tools (what the model is allowed to see)

The second lever was tool visibility.

We already assigned tools per graph node: each agent got a bundle that matched its job. That was a good baseline, but it was still coarse. In practice, we wanted finer rules: do not even present a tool until its preconditions are true, or hide whole families of tools after the conversation has ruled them out.

A concrete case: if someone told us early that they do not receive SMS at their number, we should stop offering anything that implies texting that channel. The right fix is not only “don’t do it” in the prompt; it is to remove those tools from the model-visible set so they are not competing for attention or getting called by mistake.

We moved toward variable tools: the visible tool list becomes a function of state, not only of node identity. After a particular tool runs, we can reset what is exposed so only the tools that still make sense remain in play. That is progressive disclosure applied to affordances: the model literally cannot reach for a capability we have not disclosed yet, or that we have already retired for this thread.

Together with sharper system messages, this reduced accidental tool calls and made routing decisions look less like fuzzy inference and more like following a gated workflow.


What we got out of it

Neither idea was a rewrite of the graph. They were targeted upgrades to how we disclosed state and capability at transitions.

Routing reliability moved up sharply—fewer wrong turns at handoffs, fewer calls that were valid in isolation but wrong in sequence. Tool-call accuracy improved alongside that, and the conversations themselves felt more grounded: less thrashing, less “helpful” behavior that ignored something we had already established.

If there is a single lesson I would repeat on the next agentic project, it is this: progressive disclosure is not only about how much text you put in the window. It is about when you inject instructions, and what you allow the model to even consider doing. System messages and variable tools were our way of making that explicit in code—and it made the graph feel finally as precise as we had intended it to be.